package com.gyp.utils;

import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.gyp.bo.WxPayJsApiAmountBO;
import com.gyp.bo.WxPayJsApiOrderBO;
import com.gyp.bo.WxPayPayerBO;
import com.gyp.constant.WxPayJsApiConstant;
import com.gyp.vo.pay.WxPayJsApiPreVO;
import com.wechat.pay.contrib.apache.httpclient.WechatPayHttpClientBuilder;
import com.wechat.pay.contrib.apache.httpclient.auth.*;
import com.wechat.pay.contrib.apache.httpclient.cert.CertificatesManager;
import com.wechat.pay.contrib.apache.httpclient.exception.HttpCodeException;
import com.wechat.pay.contrib.apache.httpclient.exception.NotFoundException;
import com.wechat.pay.contrib.apache.httpclient.util.PemUtil;
import lombok.Data;
import lombok.RequiredArgsConstructor;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.system.ApplicationHome;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.stereotype.Component;

import java.io.*;
import java.net.URISyntaxException;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.util.*;

/**
 * @ClassName WxPayJsApiCreateSignUtil
 * @Description JSAPI工具类
 * @Author 俞春旺
 * @Company RBGT
 * @Date 2023年4月21日 0021 上午 08:58:36
 * @Version 1.0
 */
@Component
@Data
@RequiredArgsConstructor
public class WxPayJsApiHttpUtil {

    private static Logger logger = LoggerFactory.getLogger(WxPayJsApiHttpUtil.class);
    private final WxPayJsApiSignUtil wxPayJsApiSignUtil;
    /**
     * appId
     **/
    @Value("${wx.pay.appId}")
    String appId;
    /**
     * mchId
     **/
    @Value("${wx.pay.merchantId}")
    String mchId;
    /**
     * mchSerialNo
     **/
    @Value("${wx.pay.merchantSerialNumber}")
    String mchSerialNo;
    /**
     * apiV3Key
     **/
    @Value("${wx.pay.apiV3key}")
    String apiV3Key;
    /**
     * notifyUrl
     **/
    @Value("${wx.pay.notifyUrl}")
    String notifyUrl;

    /**
     * 操作 - 微信JS_API下单
     *
     * @return com.ycg.pay.model.vo.WxPayJsApiPreVO
     * @Author 俞春旺
     * @Date 下午 04:38:03 2023年4月22日 0022
     **/
    public WxPayJsApiPreVO jsApiV3() throws Exception {
        //请求URL
        HttpPost httpPost = new HttpPost(WxPayJsApiConstant.JS_API_URL);
        String out_trade_no = DateUtil.format(new Date(), DatePattern.PURE_DATETIME_MS_PATTERN);
        // 请求body参数
        WxPayJsApiAmountBO amount = new WxPayJsApiAmountBO();
        amount.setTotal(1);
        amount.setCurrency("CNY");

        WxPayPayerBO payer = new WxPayPayerBO();
        payer.setOpenid("oqcOx61ZPCM-N6RuL26dE3pnHhA4");

        WxPayJsApiOrderBO request = new WxPayJsApiOrderBO();
        request.setMchid(mchId);
        request.setOut_trade_no(out_trade_no);
        request.setAttach("order_no_20230423");
        request.setAppid(appId);
        request.setDescription("练视频推送套餐");
        request.setNotify_url(notifyUrl);
        request.setAmount(amount);
        request.setPayer(payer);

        StringEntity entity = new StringEntity(JSONUtil.toJsonStr(request), "utf-8");
        entity.setContentType("application/json");
        httpPost.setEntity(entity);
        httpPost.setHeader("Accept", "application/json");

        // 构建 - 请求信息
        CloseableHttpClient closeableHttpClient = initCloseableHttpClientV3();
        // 操作 - 完成请求
        CloseableHttpResponse response = closeableHttpClient.execute(httpPost);
        String bodyAsString = EntityUtils.toString(response.getEntity());
        System.out.println(bodyAsString);
        try {
            int statusCode = response.getStatusLine().getStatusCode();
            if (statusCode == 200) {
                System.out.println("success,return body = " + EntityUtils.toString(response.getEntity()));
            } else if (statusCode == 204) {
                System.out.println("success");
            } else {
                System.out.println("failed,resp code = " + statusCode + ",return body = " + EntityUtils.toString(response.getEntity()));
                throw new IOException("request failed");
            }
        } finally {
            response.close();
            closeableHttpClient.close();
        }
        // 构造 - 响应信息
        WxPayJsApiPreVO result = convertWxPayJsApiPreInfo((String) JSONUtil.parseObj(bodyAsString).get("prepay_id"));
        System.out.println("result -> " + JSONUtil.toJsonStr(result));
        return result;
    }

    /**
     * 获取 - jsApi加密串及前端换起JSAPI支付界面
     *
     * @param prepayId
     * @return com.ycg.pay.model.vo.WxPayJsApiPreVO
     * @Author 俞春旺
     * @Date 下午 01:42:42 2023年4月21日 0021
     **/
    protected WxPayJsApiPreVO convertWxPayJsApiPreInfo(String prepayId) throws Exception {
        logger.info("[开始]预支付信息-prepayId : {}", prepayId);
        // 构建 - 相关参数
        String time = String.valueOf(System.currentTimeMillis() / 1000);
        String nonceStr = UUID.randomUUID().toString().replace("-", "");
        String packageStr = StrUtil.format(WxPayJsApiConstant.SIGN_PACKAGE_STR, prepayId);
        // 构建 - 签名信息
        ArrayList<String> list = new ArrayList<>();
        list.add(appId);
        list.add(time);
        list.add(nonceStr);
        list.add(packageStr);

        // 操作 - 签名
        String packageSign = wxPayJsApiSignUtil.signV3(WxPayJsApiSignUtil.buildSignMessage(list).getBytes());

        // 构建 - 响应信息
        WxPayJsApiPreVO result = new WxPayJsApiPreVO();
        result.setWxAppId(appId);
        result.setWxTimeStamp(time);
        result.setWxNonceStr(nonceStr);
        result.setWxPackage(packageStr);
        result.setWxSignType(WxPayJsApiConstant.SIGN_TYPE);
        result.setWxPaySign(packageSign);
        logger.info("[结束]预支付信息-WxPayJsApiPreVO : {}", JSONUtil.toJsonStr(result));
        return result;
    }

    /**
     * 查看 - JSAPI微信支付订单号查询
     *
     * @param transaction_id 微信支付订单号
     * @return com.alibaba.fastjson.JSONObject
     * @Author 俞春旺
     * @Date 下午 04:24:44 2023年4月22日 0022
     **/
    public JSONObject queryJsApiOrderInfoV3(String transaction_id) throws HttpCodeException, GeneralSecurityException, NotFoundException, IOException, URISyntaxException {
        String url = StrUtil.format(WxPayJsApiConstant.TRANSACTIONS_ORDER_QUERY_URL, transaction_id, mchId);
        URIBuilder uriBuilder = new URIBuilder(url);
        HttpGet httpGet = new HttpGet(uriBuilder.build());
        httpGet.addHeader("Accept", "application/json");

        CloseableHttpClient httpClient = initCloseableHttpClientV3();
        CloseableHttpResponse response = httpClient.execute(httpGet);

        String bodyAsString = EntityUtils.toString(response.getEntity());
        System.out.println("bodyAsString:" + bodyAsString);
        return JSONUtil.parseObj(bodyAsString);
    }

    /**
     * 查询 - 实时获取resources实时新增的文件目录
     *
     * @param
     * @return java.lang.String
     * @Author 俞春旺
     * @Date 下午 07:29:28 2023年3月30日 0030
     **/
    protected static String getResourcesPath() {
        // 这里需要注意的是ApplicationHome是属于SpringBoot的类
        // 获取项目下resources/static/img路径
        ApplicationHome applicationHome = new ApplicationHome(WxPayJsApiHttpUtil.class);

        // 保存目录位置根据项目需求可随意更改 - win和linux目录不一样
        String path = applicationHome.getDir().getParentFile().getParentFile().getAbsolutePath() + "/src/main/resources/pay/";
        System.out.println("path:" + path);
        return path;
    }

    /**
     * 构造 - 通用CloseableHttpClient（定时更新平台证书功能）
     *
     * @return org.apache.http.impl.client.CloseableHttpClient
     * @Author 俞春旺
     * @Date 下午 04:31:47 2023年4月22日 0022
     **/
    protected CloseableHttpClient initCloseableHttpClientV3() throws IOException, HttpCodeException, GeneralSecurityException, NotFoundException {
        // 获取 - 秘钥证书信息
        PrivateKey merchantPrivateKey = PemUtil.loadPrivateKey(new FileInputStream(WxPayJsApiHttpUtil.getResourcesPath() + "apiclient_key.pem"));
        // 获取 - 证书管理器实例
        CertificatesManager certificatesManager = CertificatesManager.getInstance();

        // 操作 - 向证书管理器增加需要自动更新平台证书的商户信息
        certificatesManager.putMerchant(mchId, new WechatPay2Credentials(mchId,
                new PrivateKeySigner(mchSerialNo, merchantPrivateKey)), apiV3Key.getBytes(StandardCharsets.UTF_8));
        // ... 若有多个商户号，可继续调用putMerchant添加商户信息
        // 操作 - 从证书管理器中获取verifier
        Verifier verifier = certificatesManager.getVerifier(mchId);
        WechatPayHttpClientBuilder builder = WechatPayHttpClientBuilder.create()
                .withMerchant(mchId, mchSerialNo, merchantPrivateKey)
                .withValidator(new WechatPay2Validator(verifier));

        // 构造通过WechatPayHttpClientBuilder构造的HttpClient，会自动的处理签名和验签，并进行证书自动更新
        return builder.build();
    }
}
